package cn.fenqing168.bio.groupchat;

import lombok.SneakyThrows;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;

/**
 * 服务端
 * @author fenqing
 */
public class GroupchatServer {

    /**
     * 公用选择器
     */
    private Selector selector;
    /**
     * 服务端通道
     */
    private ServerSocketChannel listenChannel;
    /**
     * 监听端口
     */
    private final static int PORT = 9999;

    /**
     * 构造器
     */
    public GroupchatServer(){
        try{
            //打开一个选择器
            selector = Selector.open();

            //打开一个通道
            listenChannel = ServerSocketChannel.open();
            //监听端口
            listenChannel.bind(new InetSocketAddress(PORT));
            //设置非堵塞
            listenChannel.configureBlocking(false);
            //注册到选择器，关注连接事件
            listenChannel.register(selector, SelectionKey.OP_ACCEPT);

        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 监听方法
     */
    public void listen(){
        System.out.println("开始监听！");
        //循环获取有事件的通道
        try{
            while (selector.select() > 0){
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                //遍历有事件的通道
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()){
                    SelectionKey selectionKey = iterator.next();
                    //如果是连接事件
                    if(selectionKey.isAcceptable()){
                        //获取通道
                        ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
                        SocketChannel socketChannel = serverSocketChannel.accept();
                        //设置非堵塞
                        socketChannel.configureBlocking(false);
                        //注册到selector
                        socketChannel.register(selector, SelectionKey.OP_READ);
                        System.out.println(socketChannel.getRemoteAddress() + " 上线 ");
                    } else if (selectionKey.isReadable()){
                        //如果是读事件
                        readData(selectionKey);
                    }
                    iterator.remove();
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 读取消息，并转发
     * @param key 有事件的通道
     */
    @SneakyThrows
    private void readData(SelectionKey key){
        //获取到通道
        SocketChannel channel = (SocketChannel) key.channel();
        if(!channel.isConnected()){
            key.cancel();
            return;
        }
        //创建缓冲区
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        //循环读取数据
        try{
            while ( channel.read(byteBuffer) > 0){
                baos.write(byteBuffer.array(), 0, byteBuffer.position());
                //读取完毕后清空
                byteBuffer.clear();
            }
        }catch (Exception e){
            channel.close();
            key.cancel();
            return;
        }

        String msg = baos.toString();
        System.out.println("读取到数据: " + msg);
        //给其他通道写数据
        sendInfoToOtherChannel(msg, channel);
    }

    private void sendInfoToOtherChannel(String msg, SocketChannel slef){
        Set<SelectionKey> keys = selector.keys();
        for (SelectionKey key : keys) {
            SelectableChannel channel = key.channel();
            if(channel instanceof SocketChannel && channel != slef){
                SocketChannel channel1 = (SocketChannel) channel;
                try {
                    channel1.write(ByteBuffer.wrap(msg.getBytes(StandardCharsets.UTF_8)));
                } catch (IOException e) {
                    try {
                        System.out.println(channel1.getRemoteAddress() + " 下线了 ");
                        key.cancel();
                        channel1.close();
                    } catch (IOException ioException) {
                        ioException.printStackTrace();
                    }
                }
            }
        }
    }

    public static void main(String[] args) {
        GroupchatServer groupchatServer = new GroupchatServer();
        groupchatServer.listen();
    }

}
